iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
Modern Web

web3 短篇集系列 第 27

使用 eth_getProof 證明帳戶的歷史餘額

  • 分享至 

  • xImage
  •  

國慶日來介紹 eth_getProof 這個 JSON RPC API,它可以向節點請求某地址在某區塊的資料證明。今天實作使用 ethereumjs 來驗證 Merkle Patricia Trie 的 proof,最後我們要確保 proof 的 stateRoot 跟我們要驗證的 blockNumber 的 stateRoot 是一致的。

EIP-1186: RPC-Method to get Merkle Proofs - eth_getProof (2018)

使用 ethers v6 呼叫 JSON RPC API:

const proof = await provider.send('eth_getProof', [address, [], toBeHex(blockNumber)])

eth_getProof

Parameters

  • address: 帳戶或合約地址。
  • storageKeys: 用於合約的 storage slot 鍵值對的「鍵」
  • blockNumber: ex. 'latest'

Returns

  • address: 同上
  • accountProof: RLP 序列化後的節點陣列,第一個是 stateRoot,要將 RLP 編碼做雜湊才能得到 stateRoot hash。
  • balance: 帳戶在該 blockNumber 的餘額。
  • codeHash: 如果是合約,就是合約 bytecode 的雜湊值,如果是 EOA,就是空字串的雜湊值,等於 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
  • nonce
  • storageHash
  • storageProof
    • key
    • value
    • proof

名詞解釋

  • MPT: Merkle Patricia Trie - 以太坊協議所使用的 Merkle Tree,每一個 block header 有三顆樹的根:
    • stateRoot
    • transactionRoot
    • receiptRoot
  • RLP: Recursive Length Prefix - 以太坊協議所使用的編碼格式。
  • Account: 每一個帳戶或合約地址,都存有四個值:
    • nonce
    • balance
    • storageHash
    • codeHash

實作驗證

這段程式碼要證明 0xd78B5013757Ea4A7841811eF770711e6248dC282 在 Sepolia blockNumber 6848860 有 1.639982991581458239 ETH。

import { Trie } from '@ethereumjs/trie'
import { bytesToHex, hexToBytes } from '@ethereumjs/util'
import { decodeRlp, JsonRpcProvider, keccak256, toBeHex } from 'ethers'

// prove 0xd78B5013757Ea4A7841811eF770711e6248dC282 has 1639982991581458239 wei in block 6848860

const provider = new JsonRpcProvider(process.env.sepolia)
const address = process.env.dev!
const blockNumber = 6848860

const block = await provider.getBlock(blockNumber)
if (!block) throw new Error('block not found')
const stateRoot = block.stateRoot
console.log(`stateRoot at ${blockNumber}`, stateRoot)

const proof = await provider.send('eth_getProof', [address, [], toBeHex(blockNumber)])

console.log(proof)

// ==================================== verify proof ========================================

const addressHash = keccak256(address)

const uint8ArrayProof = proof.accountProof.map((node: string) => hexToBytes(node))

const value = await Trie.verifyProof(hexToBytes(addressHash), uint8ArrayProof)
const decoded = decodeRlp(bytesToHex(value!))
console.log('decoded', decoded)
// [nonce, balance, storageHash, codeHash]

// the hash of the accountProof's first node is the stateRoot
if (keccak256(proof.accountProof[0]) === stateRoot) {
	console.log('verified')
}

Reference

後記

今日實作的過程在 hex, bytes, RLP, keccak256 之間的轉換花了好多時間才弄懂,對以太坊協議層和 bytes 的操作真的不太熟,驗證失敗了好多次,好在最後有驗證成功 :)


上一篇
部署合約 (create & create2)
下一篇
案例研究:Create2 Vickrey Auction
系列文
web3 短篇集30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言